/*******************************************************************************
 * Copyright (c) 2010 Marcelo Cataldo, Sangeeth Nambiar
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * DepsGraph.java
 * 
 * Implements an in-core abstraction layer for the dependency data loaded
 * from an XML file or a DB connection
 * 
 * Contributors:
 *    Marcelo Cataldo  - initial implementation 
 *    Sangeeth Nambiar - initial implementation
 *******************************************************************************/
package edu.cmu.isr.dinvenio.model;

import java.util.Enumeration;
import java.util.Hashtable;
import org.eclipse.swt.widgets.Composite;

import edu.cmu.isr.dinvenio.exceptions.InvalidObjectTypeException;
import edu.cmu.isr.dinvenio.exceptions.ObjectNotFoundException;

public class DepsGraph {
	@SuppressWarnings("unused")
	private Composite myParent;
	
	// Tables with contents of this graph
	public Hashtable<String,DepsGraphNodeSet> nodeSets;	// contains the actual in-core representation of a node
	public Hashtable<String,DepsGraphLayer> layers;     // header for a graph in-core representation of a graph	
	// Tables for graph properties
	private Hashtable<String,DepsGraphProperty> availableNodeProperties;// header for a graph in-core representation of a graph
	public Hashtable<String,java.awt.Color> colorPropertyMap;
	
	private String nodeSizeProperty; 		// name of node property that determines the size of the node
	private double nodeSize_max;	// max/min of the property values to be used in drawing the graph
	private double nodeSize_min;
	
	private String nodeColorProperty;		// name of node property that determines the color of the node
	private double nodeColor_max;	// max/min of the property values to be used in drawing the graph
	private double nodeColor_min;
	
	private static final int NODE_SIZE_PROPERTY   = 1;
	private static final int NODE_COLOR_PROPERTY  = 2;
	
	/*
	 * Constructors
	 */
	public DepsGraph() {
		nodeSets                = new Hashtable<String,DepsGraphNodeSet>();
		layers                  = new Hashtable<String,DepsGraphLayer>();
		availableNodeProperties = new Hashtable<String,DepsGraphProperty>();
		colorPropertyMap        = new Hashtable<String,java.awt.Color>();

		nodeSizeProperty = null;
		nodeColorProperty = null;
	}
	
	/*
	 * ACCESSOR methods
	 */
	public String getPropertyForNodeSize()
	{
		return nodeSizeProperty;
	}
	
	public void setPropertyForNodeSize(String p)
	{
		nodeSizeProperty = p;
		if (p != null) {
			this.refreshPropertyNodeSize();
		}
	}

	public double getNodeSizeMax() {
		return nodeSize_max;
	}

	public double getNodeSizeMin() {
		return nodeSize_min;
	}

	public String getPropertyForNodeColor()
	{
		return nodeColorProperty;
	}
	
	public void setPropertyForNodeColor(String p)
	{
		nodeColorProperty = p;
		if (p != null) {
			this.refreshPropertyNodeColor();
		}		
	}
	
	public double getNodeColorMax() {
		return nodeColor_max;
	}

	public double getNodeColorMin() {
		return nodeColor_min;
	}
	
	public String getPropertyForEdgeColor(String lid)
	{
		DepsGraphLayer l = layers.get(lid);
		if (l == null) { return null; }
		return l.getPropertyForEdgeColor();
	}

	public void setPropertyForEdgeColor(String lid, String p)
	{
		DepsGraphLayer l = layers.get(lid);
		if (l != null) { 
			l.setPropertyForEdgeColor(p);
		}
	}
	
	public DepsGraphNodeSet getNodeSet(String id) 
	{
		return nodeSets.get(id);
	}

	public Hashtable<String, DepsGraphNodeSet> getAllNodeSets() 
	{
		return nodeSets;
	}

//	public DepsGraphNode getNode(String nid) throws ObjectNotFoundException
//	{
//		String nodeSet = DepsGraphNode.getKeyNodeSetName(nid);
//		DepsGraphNodeSet ns = nodeSets.get(nodeSet);
//		if(ns ==null) { throw new ObjectNotFoundException("Invalid Nodeset in node id " + nid); }
//		return ns.getNodeByKey(nid);
//	}
	
	public Enumeration<String> getAvailableNodeProperties()
	{
		return this.availableNodeProperties.keys();
	}
	
	public DepsGraphLayer getLayer(String id) 
	{
		return layers.get(id);
	}

	public Hashtable<String, DepsGraphLayer> getAllLayers() 
	{
		return layers;
	}

//	@SuppressWarnings("unchecked")
//	public Enumeration<String> getTechnicalLayerIDs()
//	{
//		ArrayList<String> s = new ArrayList<String>();
//		Enumeration<DepsGraphLayer> ll = layers.elements();
//		while(ll.hasMoreElements()) {
//			DepsGraphLayer l = ll.nextElement();
//			if (l.isTechnicalLayer()) {
//				s.add(l.getID());
//			}
//		}
//		Enumeration e = Collections.enumeration(s);
//		return e;
//	}
	
//	@SuppressWarnings("unchecked")
//	public Enumeration<String> getOrganizationalLayerIDs()
//	{
//		ArrayList<String> s = new ArrayList<String>();
//		Enumeration<DepsGraphLayer> ll = layers.elements();
//		while(ll.hasMoreElements()) {
//			DepsGraphLayer l = ll.nextElement();
//			if (l.isOrganizationalLayer() || l.isBipartiteLayer()) {
//				s.add(l.getID());
//			}
//		}
//		Enumeration e = Collections.enumeration(s);
//		return e;
//	}

	/*
	 * Methods for ADDING elements
	 */
	public void addNodeSet(String nstype, String nsname) {
		try {
			DepsGraphNodeSet ns = new DepsGraphNodeSet(nsname, nstype);
			nodeSets.put(nsname, ns);
		}
		catch (InvalidObjectTypeException e) {
			e.printStackTrace();
		}
	}
	
	public void addNode(String nid, String nsName) throws ObjectNotFoundException
	{
		DepsGraphNode n = new DepsGraphNode(nid, nsName);
		DepsGraphNodeSet ns = nodeSets.get(nsName);
		if (ns == null) {
			throw new ObjectNotFoundException("DepsGraph.addNode(01): Invalid nodeSet Name " + nsName + ".");
		}
		ns.addNode(n);
	}

	public void addNodeProperty(String nsid, String nid, String pn, String pt, String pv)
	{
		DepsGraphNodeSet ns = nodeSets.get(nsid);
		if (ns != null) {
			ns.addNodeProperty(nid, pn, pt, pv);
		}
		else {
			System.out.println("DepsGraph.addNode(01): Malformed node Name " + nid + ".");
		}
		// If this property is not already in the list then add it
		if(!availableNodeProperties.containsKey(pn)){
			availableNodeProperties.put(pn, new DepsGraphProperty(pn, pt, pv));
		}
	}

	public void addEdge(String lid, String s, String d, double v) throws NullPointerException,ObjectNotFoundException
	{
		DepsGraphLayer l = layers.get(lid);
		if (l == null) { throw new NullPointerException(); }
		
		DepsGraphNodeSet nsSource = nodeSets.get(l.getSourceNodeSetId());
		if (nsSource == null) { throw new NullPointerException(); }
		DepsGraphNodeSet nsTarget = nodeSets.get(l.getTargetNodeSetId());
		if (nsTarget == null) { throw new NullPointerException(); }
		
		DepsGraphNode n;
		// Make sure the source node exists
		n = nsSource.getNodeByName(s);
		if (n == null) {
			throw new ObjectNotFoundException("DepsGraph.addEdge: Source Node " + s + " not found");
		}
		// Make sure the target node exists
		n = nsTarget.getNodeByName(d);
		if (n == null) {
			throw new ObjectNotFoundException("DepsGraph.addEdge: Target Node " + d + " not found");
		}
		// add edge
		l.addEdge(s, d, v);
	}

	public void addLayer(String lid, String sName, String dName, String color) throws ObjectNotFoundException
	{
		DepsGraphNodeSet nsSource = nodeSets.get(sName);
		if (nsSource == null) {
			throw new ObjectNotFoundException("DepsGraph.addLayer: Source NodeSet" + sName + " not found");
		}
		DepsGraphNodeSet nsTarget = nodeSets.get(dName);
		if (nsTarget == null) {
			throw new ObjectNotFoundException("DepsGraph.addLayer: Target NodeSet" + dName + " not found");
		}

		DepsGraphLayer l = new DepsGraphLayer(lid, nsSource, nsTarget);
		l.setEdgeColor(color);
		layers.put(lid, l);
	}

	/*
	 * Methods for VISUALIZATION
	 */
	public void enableLayer(String lid)
	{
		DepsGraphLayer l = layers.get(lid);
		if (l != null) {
			l.enableLayer();
		}
	}

	public void disableLayer(String lid)
	{
		DepsGraphLayer l = layers.get(lid);
		if (l != null) {
			l.disableLayer();
		}
	}
	
	public void setLayerVisibility(String lid, boolean visibility)
	{
		DepsGraphLayer l = layers.get(lid);
		if (l != null) {
			l.setVisibility(visibility);
		}
	}
	
	public Enumeration<String> getLayerIDs()
	{
		return this.layers.keys();
	}
		
	public void updateNodeDegrees()
	{
		// iterate on the graph layers
		Enumeration<DepsGraphLayer> llist = this.layers.elements();
		while(llist.hasMoreElements()) {
			DepsGraphLayer l = llist.nextElement();
			if (l.getEnableStatus()) { l.updateNodeDegrees(); }
		}
	}
	
	private void refreshPropertyNode(int type)
	{
		String p = null;
		double min = Double.MAX_VALUE;
		double max = 0;
		
		if (type == NODE_SIZE_PROPERTY) { p = nodeSizeProperty; }
		if (type == NODE_COLOR_PROPERTY) { p = nodeColorProperty; }
		if (p != null) {
			Enumeration<DepsGraphNodeSet> lns = nodeSets.elements();
			while(lns.hasMoreElements()) {
				DepsGraphNodeSet ns = lns.nextElement();
				Enumeration<DepsGraphNode> lnode = ns.getNodes();
				while(lnode.hasMoreElements()) {
					DepsGraphNode n = lnode.nextElement();
					String pt = n.getPropertyType(p);
					if (pt != null) {
						if((!n.getPropertyValue(p).isEmpty()) && (type == NODE_COLOR_PROPERTY)){
							colorPropertyMap.put(n.getPropertyValue(p), java.awt.Color.magenta);
						}				
						if (pt.equals("double")) {
							double val = Double.parseDouble(n.getPropertyValue(p));
							if (min > val) { min = val; }
							if (max < val) { max = val; }
						}
					}
				}
			}
			if (type == NODE_SIZE_PROPERTY) { 
				nodeSize_max = max; 
				nodeSize_min = min; 
			}
			if (type == NODE_COLOR_PROPERTY) { 
				nodeColor_max = max; 
				nodeColor_min = min;
				updatecolorPropertyMap();
			}
		}
	}
	
	private void updatecolorPropertyMap()
	{
		int i = 0;	
		Enumeration<String> e = colorPropertyMap.keys();
		while (e.hasMoreElements()) {
			String key = e.nextElement();
			float hue = i/ (float)(colorPropertyMap.size());
			java.awt.Color cl = java.awt.Color.getHSBColor(hue, 1.0f, 1.0f);
			colorPropertyMap.put(key, cl);
			i++;
		}
	}
	public java.awt.Color getColorFromTable(String key){
		return colorPropertyMap.get(key);
	}
	
	public void refreshPropertyNodeSize()
	{
		if (nodeSizeProperty != null) {
			this.refreshPropertyNode(NODE_SIZE_PROPERTY);
		}
	}

	public void refreshPropertyNodeColor()
	{
		if (nodeColorProperty != null) {
			this.refreshPropertyNode(NODE_COLOR_PROPERTY);
		}
	}
}
